package com.youpai.platform.vulture.common.handler;

import com.alibaba.fastjson.JSONObject;
import com.youpai.platform.vulture.common.constant.CustomConstants;
import com.youpai.platform.vulture.common.constant.NumberConstants;
import com.youpai.platform.vulture.common.entity.AccessMessage;
import com.youpai.platform.vulture.common.entity.Header;
import com.youpai.platform.vulture.common.enums.MsgFlagEnum;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.util.CharsetUtil;

import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.youpai.platform.vulture.common.util.PrimaryKeyUtil.*;

/**
 * @date: 2022/6/22 20:54
 * @author: Anna
 * @version: 1.0
 * @description: 自定义协议decoder
 *         <p>
 *         ———————————————————----------------------------
 *         |  key   | flag     | length   |header| content|
 *         |_1字节__|___1字节__|__2字节__|_______|________|
 */
public class CustomMessageDecoder extends LengthFieldBasedFrameDecoder {

    /**
     * 字节序
     */
    private static final ByteOrder BYTE_ORDER = ByteOrder.BIG_ENDIAN;
    /**
     * 包最大 8kb
     */
    private static final int MAX_FRAME_LENGTH = 8192 * 10;
    /**
     * 长度域偏移量0
     */
    private static final int LENGTH_FIELD_OFFSET = CustomConstants.PRIMARY_KEY_LENGTH + CustomConstants.FLAG_LENGTH;
    /**
     * 长度域长度 2字节
     */
    private static final int LENGTH_FILE_LENGTH = CustomConstants.LENGTH_FIELD_LENGTH;
    /**
     * 长度修正 0
     */
    private static final int LENGTH_ADJUSTMENT = NumberConstants.INT_ZERO;
    /**
     * 值偏移量
     */
    private static final int INITIAL_BYTES_TO_STRIP = LENGTH_FIELD_OFFSET + LENGTH_FILE_LENGTH;
    /**
     * 快速失败
     */
    private static final boolean FAIL_FAST = true;

    public CustomMessageDecoder() {
        super(BYTE_ORDER, MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FILE_LENGTH, LENGTH_ADJUSTMENT,
                INITIAL_BYTES_TO_STRIP, FAIL_FAST);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        try {
            return doDecode(ctx, in);
        } finally {
            cleanAllTL();
        }
    }

    private Object doDecode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        // 读取key
        byte key = in.getByte(NumberConstants.INT_ZERO);
        keyTL.set(key);
        bufTL.set(in);
        // 读取标志位
        int flag = getUnsignedByte(NumberConstants.INT_ONE);
        if (MsgFlagEnum.HEART_BEAT_PONG.flag == flag || MsgFlagEnum.HEART_BEAT_PING.flag == flag) {
            in.skipBytes(NumberConstants.INT_ONE);
            return AccessMessage.build().setFlag(readUnsignedByte());
        }
        ByteBuf buf = null;
        try {
            // 只取content
            buf = (ByteBuf) super.decode(ctx, in);
            if (null == buf) {
                return null;
            }
            bufTL.set(buf);
            // 解码
            return decodeAccessMessage(flag);
        } finally {
            if (null != buf) {
                buf.release();
            }
        }
    }

    private Object decodeAccessMessage(int flag) {
        AccessMessage result = new AccessMessage();
        int headerCount = readUnsignedByte();
        // 设置flag
        result.setFlag(flag);
        // 解析header
        List<Header> headerList = decodeHeaderList(headerCount);
        result.setHeaderList(headerList);
        if (NumberConstants.INT_ZERO != bufTL.get().readableBytes()) {
            // 解析body
            String body = decodeBody();
            result.setBody(body);
        }
        return result;
    }

    private String decodeBody() {
        byte[] remainingPartBytes = remainingPartBytes();
        return new String(remainingPartBytes, Charset.defaultCharset());
    }

    private List<Header> decodeHeaderList(int headerCount) {
        if (NumberConstants.INT_ZERO == headerCount) {
            return Collections.emptyList();
        }
        List<Header> headerList = new ArrayList<>(headerCount);
        for (int i = 0; i < headerCount; i++) {
            Header header = Header.build()
                    .setHeaderType(readUnsignedByte())
                    .setHeaderValue(JSONObject.parse(new String(readBytes(readUnsignedByte()), CharsetUtil.UTF_8)));
            headerList.add(header);
        }
        return headerList;
    }
}
